/*
	some common api for change string between int/32/64,uint8/16/32/64,float32/64
*/
package common

import (
	"World/common/beegomap"
	"World/common/logger"
	pb "World/pb"
	"bytes"
	"encoding/binary"
	"fmt"
	"math/rand"
	"strconv"
	"strings"
	"time"
	"unicode"

	"github.com/golang/protobuf/proto"
)

var (
	PlayerMap *beegomap.BeeMap //  = make(map[int]int)
	SuitMap   map[int32]string
	RankMap   map[int32]string
)

func init() {
	PlayerMap = beegomap.NewBeeMap()
	SuitMap = map[int32]string{0: "方块", 1: "梅花", 2: "红桃", 3: "黑桃"}
	RankMap = map[int32]string{0: "2", 1: "3", 2: "4", 3: "5", 4: "6", 5: "7", 6: "8", 7: "9", 8: "10", 9: "J", 10: "Q", 11: "K", 12: "A"}
}

func Atoi(s string) int {
	if s == "" {
		return 0
	}

	i, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return i
}

func Atouint(s string) uint {
	return uint(Atoi(s))
}

func Atoi64(s string) int64 {
	if s == "" {
		return 0
	}

	i, err := strconv.ParseInt(s, 10, 0)

	if err != nil {
		panic(err)
	}
	return i
}

func Atof32(s string) float32 {
	f, err := strconv.ParseFloat(s, 32)
	if err != nil {
		panic(err)
	}
	return float32(f)
}

func Atof64(s string) float64 {
	f, err := strconv.ParseFloat(s, 64)
	if err != nil {
		panic(err)
	}
	return f
}

func Atoi32(s string) int32 {
	i, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return int32(i)
}

func Atobyte(s string) byte {
	i, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return byte(i)
}

func Itoa(n int) string {
	s := strconv.Itoa(n)
	return s
}

func I32toa(n int32) string {
	return fmt.Sprintf("%d", n)
}

func I64toa(i int64) (s string) {
	s = fmt.Sprintf("%d", i)
	return s
}

func String2IntArray(s string) (ar []int) {
	for _, vId := range strings.Split(s, ",") {
		ar = append(ar, Atoi(vId))
	}
	return ar
}

func IntArray2String(ar []int) string {
	vs := []string{}
	for _, a := range ar {
		vs = append(vs, I64toa(int64(a)))
	}
	return strings.Join(vs, ",")
}

func String2Int32Array(s string) (ar []int32) {
	for _, vId := range strings.Split(s, ",") {
		ar = append(ar, Atoi32(vId))
	}
	return ar
}

func Int32Array2String(ar []int32) string {
	vs := []string{}
	for _, a := range ar {
		vs = append(vs, I64toa(int64(a)))
	}
	return strings.Join(vs, ",")
}

func String2Int64Array(s string) (ar []int64) {
	for _, vId := range strings.Split(s, ",") {
		ar = append(ar, Atoi64(vId))
	}
	return ar
}

func StringArray2Int64Array(ss []string) (ar []int64) {
	ar = make([]int64, len(ss))
	for index, s := range ss {
		ar[index] = Atoi64(s)
	}
	return
}

func Int64Array2String(ar []int64) string {
	vs := []string{}
	for _, a := range ar {
		vs = append(vs, I64toa(a))
	}
	return strings.Join(vs, ",")
}

// Int2Fuzzy 3 -> (?,?,?)
func Int2Fuzzy(ir int) string {
	fuzAr := make([]string, ir)
	for i := 0; i < ir; i++ {
		fuzAr[i] = "?"
	}
	return "(" + strings.Join(fuzAr, ",") + ")"
}

func AInt2AInt64(ar []int) (br []int64) {
	br = make([]int64, len(ar))
	for index, r := range ar {
		br[index] = int64(r)
	}
	return
}

func UnderScore2Calm(s string) string {
	words := strings.Split(s, "_")
	var newWords []string

	for _, word := range words {
		a := []rune(word)
		a[0] = unicode.ToUpper(a[0])
		newWords = append(newWords, string(a))
	}

	return strings.Join(newWords, "")
}

func Calm2UnderScore(s string) string {
	var words []string
	for i := 0; s != ""; s = s[i:] {
		i = strings.IndexFunc(s[1:], unicode.IsUpper) + 1
		if i <= 0 {
			i = len(s)
		}
		words = append(words, strings.ToLower(s[:i]))
	}
	return strings.Join(words, "_")
}

func Atob(str string) bool {
	if str == "1" {
		return true
	}

	return false
}

func Atoas(str string) []string {
	return strings.Split(str, "|")
}

func Atoi64s(str string) []int64 {
	as := strings.Split(str, "|")
	is := make([]int64, len(as))

	for i, v := range as {
		is[i] = Atoi64(v)
	}

	return is
}

func DownFirstChar(s string) string {
	cs := []rune(s)
	cs[0] = unicode.ToLower(cs[0])
	return string(cs)
}

func Singular(str string) string {
	if strings.HasSuffix(str, "es") {
		return str[0 : len(str)-2]
	}

	if strings.HasSuffix(str, "s") {
		return str[0 : len(str)-1]
	}

	return str
}

func GetCurrentUinxTimeStamp() string {
	ts := time.Now().Unix()
	stamp := fmt.Sprint(ts)
	return stamp
}

func Inet_addr(ipaddr string) uint32 {
	var (
		segments []string = strings.Split(ipaddr, ".")
		ip       [4]uint64
		ret      uint64
	)

	if len(segments) != 4 {
		return 0
	}

	for i := 0; i < 4; i++ {
		ip[i], _ = strconv.ParseUint(segments[i], 10, 64)
	}
	ret = ip[3]<<24 + ip[2]<<16 + ip[1]<<8 + ip[0]
	return uint32(ret)
}

func Inet_ntoa(ipnr uint32) string {
	var bytes [4]byte
	bytes[0] = byte(ipnr & 0xFF)
	bytes[1] = byte((ipnr >> 8) & 0xFF)
	bytes[2] = byte((ipnr >> 16) & 0xFF)
	bytes[3] = byte((ipnr >> 24) & 0xFF)

	return fmt.Sprintf("%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3])
}

func Inet_aton(ipnr string) uint32 {
	bits := strings.Split(ipnr, ".")

	b0, _ := strconv.Atoi(bits[0])
	b1, _ := strconv.Atoi(bits[1])
	b2, _ := strconv.Atoi(bits[2])
	b3, _ := strconv.Atoi(bits[3])

	var sum uint32

	sum += uint32(b3) << 24
	sum += uint32(b2) << 16
	sum += uint32(b1) << 8
	sum += uint32(b0)

	return sum
}

const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

func GetRandomString(n uint32) string {
	rand.Seed(time.Now().UnixNano())
	b := make([]byte, n)
	for i := range b {
		b[i] = alphanum[rand.Intn(len(alphanum))]
	}
	return string(b)
}

func IsIdExist(Ids []int, id int) bool {
	bExist := false

	for _, Id := range Ids {
		if Id == id {
			bExist = true
			break
		}
	}
	return bExist
}
func IsInt32IdExist(Ids []int32, id int32) bool {
	bExist := false

	for _, Id := range Ids {
		if Id == id {
			bExist = true
			break
		}
	}
	return bExist
}

func RemoveDuplicates(elements []int) []int {
	encountered := map[int]bool{}
	result := []int{}

	for v := range elements {
		if encountered[elements[v]] == true {
		} else {
			encountered[elements[v]] = true
			result = append(result, elements[v])
		}
	}
	return result
}

func RemoveDuplicateIds(elements []int32) []int32 {
	encountered := map[int32]bool{}
	result := []int32{}

	for v := range elements {
		if encountered[elements[v]] == true {
		} else {
			encountered[elements[v]] = true
			result = append(result, elements[v])
		}
	}
	return result
}

func IsPlayerInPlayerMap(uid int) bool {
	return PlayerMap.Check(uint32(uid))
}

func SendNoti(player *GamePlayer, roomid uint32, toid uint32, msgid pb.MSGID, msg interface{}) bool {
	if player == nil {
		logger.Notic("send noti failed with nil conn, playerid:%v  msgid: %v", toid, msgid)
		return false
	}
	if !IsPlayerInPlayerMap(player.UID) {
		return false
	}

	tmp, err := proto.Marshal(msg.(proto.Message))
	if err != nil {
		logger.Notic("marshal msgid:%v  response body error:%v", msgid, err)
		return false
	}
	//进行加密操作
	/*var data []byte
	if msgid == pb.MSGID_MsgID_Logon_Response {
		//rsa加密
		data, err = gorsa.RSA.PriKeyENCTYPT(tmp)
		if err != nil {
			logger.Notic("SendNoti rsa encrypted msgid:%v  response body error:%v", msgid, err)
			return false
		}
	} else {
		//aes加密
		aesData, err := goEncrypt.AesCbcEncrypt(tmp, conf.EncryptCfg.AesKey, conf.EncryptCfg.AesIv...)
		if err != nil {
			logger.Notic("SendNoti aes encrypted msgid:%v  response body error:%v", msgid, err)
			return false
		}
		bsData := base64.StdEncoding.EncodeToString(aesData)
		data = []byte(bsData)
	}*/
	data_len := len(tmp)
	//total_len := len(data) + int(Min_Message_Size)
	bys := new(bytes.Buffer)
	//binary.Write(bys, binary.BigEndian, uint16(total_len))
	binary.Write(bys, binary.BigEndian, uint16(data_len))
	binary.Write(bys, binary.BigEndian, uint16(msgid))
	binary.Write(bys, binary.BigEndian, uint32(0))
	binary.Write(bys, binary.BigEndian, toid)
	binary.Write(bys, binary.BigEndian, roomid)
	buf := append(bys.Bytes(), tmp...)

	select {
	case player.OutChannel <- buf:
		break
	case <-time.After(time.Millisecond * 20):
		logger.Notic("send to uid:%v roomid:%v timeout, channel full", toid, roomid)
		return false
	}

	return true
}
